#pragma once

#include "vector2d.h"

namespace sol9
{

	class matrix3
	{
	public:
		union
		{
			struct
			{
				f32 m11, m12, m13;
				f32 m21, m22, m23;
				f32 m31, m32, m33;
			};
			
			f32 m[3][3];
		};

		typedef void (*initializer)(matrix3&);

		static void zero(matrix3& m)
		{
			std::memset(m.m, 0, sizeof(m));
		};
		static void identity(matrix3& m)
		{
			m.makeIdentity();
		};

		public:
		// constructor
		matrix3(initializer init = zero)
		{
			init(*this);
		}

		// operators
		matrix3& operator=(const matrix3& other)
		{
			if (this == &other)
				return *this;

			std::memcpy(m, other.m, sizeof(m));
			return *this;
		}

		matrix3 operator+(const matrix3& other) const
		{
			matrix3 out;

			for (u32 i = 0; i < 9; ++i)
				out.m[0][i] = m[0][i] + other.m[0][i];

			return out;
		}

		matrix3& operator+=(const matrix3& other)
		{
			for (u32 i = 0; i < 9; ++i)
				m[0][i] += other.m[0][i];

			return *this;
		}

		matrix3 operator-(const matrix3& other) const
		{
			matrix3 out;

			for (u32 i = 0; i < 9; ++i)
				out.m[0][i] = m[0][i] - other.m[0][i];

			return out;
		}

		matrix3& operator-=(const matrix3& other)
		{
			for (u32 i = 0; i < 9; ++i)
				m[0][i] -= other.m[0][i];

			return *this;
		}

		matrix3 operator*(const matrix3& other) const
		{
			matrix3 out;

			for (u32 i = 0; i < 3; ++i)
				for (u32 j = 0; j < 3; ++j)
					for (u32 k = 0; k < 3; ++k)
						out.m[i][j] += m[k][j] * other.m[i][k];

			return out;
		}

		matrix3& operator*=(const matrix3& other)
		{
			matrix3 t = *this * other;
			std::memmove(m, t.m, sizeof(m));
			
			return *this;
		}

		matrix3 operator*(const f32& scalar) const
		{
			matrix3 out;

			for (u32 i = 0; i < 9; ++i)
				out.m[0][i] = m[0][i] * scalar;

			return out;
		}

		matrix3& operator*=(const f32& scalar)
		{
			for (u32 i = 0; i < 9; ++i)
				m[0][i] *= scalar;

			return *this;
		}

		matrix3 operator/(const f32& scalar) const
		{
			if (scalar == 0.0f)
				return *this;

			matrix3 out;

			for (u32 i = 0; i < 9; ++i)
				out.m[0][i] = m[0][i] / scalar;

			return out;
		}

		matrix3& operator/=(const f32& scalar)
		{
			if (scalar == 0.0f)
				return *this;

			for (u32 i = 0; i < 9; ++i)
				m[0][i] /= scalar;

			return *this;
		}

		// functions
		__forceinline matrix3& makeIdentity()
		{
			 m11 = 1, m12 = 0, m13 = 0;
			 m21 = 0, m22 = 1, m23 = 0;
			 m31 = 0, m32 = 0, m33 = 1;

			 return *this;
		}

		void makeTranslation(const vector2df& t)
		{
			makeIdentity();

			m31 = t.x;
			m32 = t.y;
		}

		void makeRotation(const f32& r)
		{
			makeIdentity();

			const f32 c = std::cosf(r);
			const f32 s = std::sinf(r);

			m11 = c;	m12 = s;
			m21 = -s;	m22 = c;
		}

		void makeScale(const vector2df& s)
		{
			makeIdentity();

			m11 = s.x;
			m22 = s.y;
		}
	};

} // namespace sol9